package whf.framework.report.jasper;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;

import whf.framework.config.ApplicationConfig;
import whf.framework.meta.entity.IMeta;
import whf.framework.report.ReportConstants;
import whf.framework.report.ReportDescriptor;
import whf.framework.report.ReportExporter;
import whf.framework.report.jasper.EntityCollectionDataSource;
import whf.framework.util.StringUtils;
import whf.framework.web.tag.ext.IColumn;

/**
 * @author King
 * @create Jan 18, 2008 11:22:36 AM
 */
public abstract class JasperReportExporter implements ReportExporter {
	private final static int DEFAULT_REPORT_WIDTH = 535;

	private final static int DEFAULT_HEADER_HEIGHT = 35;
	
	private static Set<String> jrxmlFiles = new HashSet<String>();

	protected ReportDescriptor reportDescriptor;
	
	protected String jrxmlFileName;
	
	protected String jasperFileName;
	/* (non-Javadoc)
	 * @see whf.framework.report.ReportExporter#generateReport(java.io.OutputStream, whf.framework.report.ReportDescriptor)
	 */
	public final void exportReport(OutputStream out) throws Exception {
		if(this.reportDescriptor == null) throw new Exception("property reportDescriptor must be set first!");
		jrxmlFileName = "";
		jasperFileName = "";
		
		IMeta meta = this.reportDescriptor.getMeta();
		if(meta != null ){
			if(!StringUtils.isEmpty(meta.getReportTemplate())) {
				jrxmlFileName = meta.getReportTemplate();
				jrxmlFiles.add(jrxmlFileName);
			} else
				jrxmlFileName = meta.getClass().getSimpleName() + "_" + meta.getId() + ".jrxml";
		}
		if(StringUtils.isEmpty(jrxmlFileName)) {
			jrxmlFileName = UUID.randomUUID().toString();
		}
		jrxmlFileName = ApplicationConfig.getInstance().getReportTemplateDirectory() + jrxmlFileName;
		if(!jrxmlFileName.endsWith(".jrxml"))
			jrxmlFileName += ".jrxml";
		
		jasperFileName = jrxmlFileName.substring(0, jrxmlFileName.length() - ".jrxml".length()) + ".jasper";
		// 判定删除缓存文件
		File jrxmlFile = new File(jrxmlFileName);
		File jasperFile = new File(jasperFileName);
		if(!jrxmlFiles.contains(jrxmlFileName)) {
			if(jrxmlFile.exists()) jrxmlFile.delete();
			if(jasperFile.exists()) jasperFile.delete();
			jrxmlFiles.add(jrxmlFileName);
		}
		//
		jrxmlFile = new File(jrxmlFileName);
		jasperFile = new File(jasperFileName);
		if(!jrxmlFile.exists()) {
			FileOutputStream fos = new FileOutputStream(jrxmlFile);
			String jrxmlString = (new JrxmlGenerator(this.reportDescriptor.getColumns())).getJrxml();
			fos.write(jrxmlString.getBytes("UTF-8"));
			fos.close();
		}
		if(!jasperFile.exists()) {
			JasperCompileManager.compileReportToFile(jrxmlFileName);
		}
		//
		JasperPrint jasperPrint = JasperFillManager.fillReport(jasperFileName, null, new EntityCollectionDataSource(this.reportDescriptor.getDataSource()));
		this.exportJasperReport(jasperPrint, out);
	}
	
	/* (non-Javadoc)
	 * @see whf.framework.report.ReportExporter#setReportDescriptor(whf.framework.report.ReportDescriptor)
	 */
	public void setReportDescriptor(ReportDescriptor descriptor) {
		this.reportDescriptor = descriptor;
	}

	abstract protected void exportJasperReport(JasperPrint jasperPrint, OutputStream out) throws Exception;

	class JrxmlGenerator {
		
		private List<IColumn> columns;

		private StringBuilder jasperXmlString = new StringBuilder();
		
		JrxmlGenerator(List<IColumn> columns) {
			this.columns = columns;
		}
		
		public String getJrxml() {
			this.beforeBody();
			this.doBody();
			return this.afterBody();
		}
		
		public void beforeBody() {
			jasperXmlString.append(parseRoot());
			jasperXmlString.append(parseQueryDefine());
			jasperXmlString.append(parseBackGround());
			jasperXmlString.append(parseTitle());
			jasperXmlString.append(parsePageheader());
			jasperXmlString.append(parseColumnHeader());
		}

		private int getHeaderHeight() {
			int columnCount = columns.size() == 0 ? 1 : columns.size() + 1;
			int perColumnWidth = DEFAULT_REPORT_WIDTH / columnCount;
			String maxLengthHeader = "";
			for (IColumn col : columns) {
				String header = col.getDisplayTitle();
				if (!StringUtils.isEmpty(header) && header.length() > maxLengthHeader.length()) {
					maxLengthHeader = header;
				}
			}
			int requiredHeaderWidth = maxLengthHeader.length() * 4;
			int headerHeight = DEFAULT_HEADER_HEIGHT;
			int lineCount = requiredHeaderWidth / perColumnWidth;
			for (int i = 0; i < lineCount; i++) {
				headerHeight += 15;
			}
			return headerHeight;
		}
		
		public void doBody() {
			String firstColBox = "<box topBorder=\"None\" topBorderColor=\"#000000\"" + " leftBorder=\"Thin\" leftBorderColor=\"#000000\""
					+ " rightBorder=\"Thin\" rightBorderColor=\"#000000\"" + " bottomBorder=\"Thin\" bottomBorderColor=\"#000000\"/>";

			String otherColBox = "<box topBorder=\"None\" topBorderColor=\"#000000\"" + " leftBorder=\"None\" leftBorderColor=\"#000000\""
					+ " rightBorder=\"Thin\" rightBorderColor=\"#000000\"" + " bottomBorder=\"Thin\" bottomBorderColor=\"#000000\"/>";
			jasperXmlString.append("<detail>").append("<band height=\"32\"  isSplitAllowed=\"true\" >");

			int size = columns.size();
			int colWidth = DEFAULT_REPORT_WIDTH / size;

			for (int i = 0; i < size; i++) {
				int start = i * colWidth;
				IColumn col = columns.get(i);

				jasperXmlString.append("<textField isStretchWithOverflow=\"true\"" + " pattern=\"\" isBlankWhenNull=\"true\" evaluationTime=\"Now\""
						+ " hyperlinkType=\"None\"  hyperlinkTarget=\"Self\" >");
				jasperXmlString.append("<reportElement mode=\"Transparent\" x=\"").append(start).append("\" y=\"0\" width=\"").append(colWidth).append("\"")
						.append(" height=\"32\" forecolor=\"#000000\" backcolor=\"#FFFFFF\" key=\"textField\" stretchType=\"RelativeToTallestObject\" ").append(
								" isPrintRepeatedValues=\"true\" isRemoveLineWhenBlank=\"false\" isPrintInFirstWholeBand=\"false\"").append(
								" isPrintWhenDetailOverflows=\"true\"/>");

				if (i == 0) {
					jasperXmlString.append(firstColBox);
				} else {
					jasperXmlString.append(otherColBox);
				}

				jasperXmlString.append("<textElement textAlignment=\"Center\" verticalAlignment=\"Middle\" rotation=\"None\" lineSpacing=\"Single\">");
				jasperXmlString.append("<font reportFont=\"DetailFont\" />");
				jasperXmlString.append("</textElement>");
				jasperXmlString.append("<textFieldExpression   class=\"java.lang.String\"><![CDATA[$F{").append(col.getProperty()).append("}]]></textFieldExpression>");
				jasperXmlString.append("</textField>");
			}
			jasperXmlString.append("</band>").append("</detail>");
		}

		public String afterBody() {
			jasperXmlString.append(parseColumnFooter());
			jasperXmlString.append(parsePageFooter());
			jasperXmlString.append(parseLastPageFooter());
			jasperXmlString.append(parseSummary());
			return jasperXmlString.toString();
		}

		public String parseRoot() {
			StringBuffer sb = new StringBuffer();
			File file = new File(jasperFileName);
			String fileName = file.getName();
			fileName = fileName.substring(0, fileName.lastIndexOf("."));
			// handle jasper report define
			sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"  ?>");
			sb.append("<jasperReport name=\"").append(fileName).append("\"").append(" language=\"java\" columnCount=\"1\" printOrder=\"Vertical\"").append(
					" orientation=\"Portrait\" pageWidth=\"595\" pageHeight=\"842\"").append(
					" columnWidth=\"535\" columnSpacing=\"0\" leftMargin=\"30\" rightMargin=\"30\"").append(
					" topMargin=\"20\" bottomMargin=\"20\" whenNoDataType=\"NoPages\" isTitleNewPage=\"false\"").append(" isSummaryNewPage=\"false\">");
			// handle property
			sb.append("<property name=\"ireport.scriptlethandling\" value=\"0\" />");
			sb.append("<property name=\"ireport.encoding\" value=\"UTF-8\" />");
			// handle import property
			sb.append("<import value=\"java.util.*\" />");
			sb.append("<import value=\"net.sf.jasperreports.engine.*\" />");
			sb.append("<import value=\"net.sf.jasperreports.engine.data.*\" />");
			// handle fonts define
			String font1 = ApplicationConfig.getInstance().getValue(ReportConstants.EXPORT_PDF_TITLE_FONT);
			String font2 = ApplicationConfig.getInstance().getValue(ReportConstants.EXPORT_PDF_BODY_FONT);
			sb.append("<reportFont name=\"TitleFont\" isDefault=\"false\" fontName=\"宋体\" size=\"18\" isBold=\"true\" isItalic=\"false\" isUnderline=\"false\" isStrikeThrough=\"false\"  pdfFontName=\""
				+ font1 + "\" pdfEncoding=\"Identity-H\" isPdfEmbedded=\"true\"/>");
			sb.append("<reportFont name=\"HeaderFont\" isDefault=\"false\" fontName=\"宋体\" size=\"10\" isBold=\"true\" isItalic=\"false\" isUnderline=\"false\" isStrikeThrough=\"false\"  pdfFontName=\""
				+ font2 + "\" pdfEncoding=\"Identity-H\" isPdfEmbedded=\"true\"/>");
			sb.append("<reportFont name=\"DetailFont\" isDefault=\"false\" fontName=\"宋体\" size=\"9\" isBold=\"false\" isItalic=\"false\" isUnderline=\"false\" isStrikeThrough=\"false\"   pdfFontName=\"STSong-Light\" pdfEncoding=\"UniGB-UCS2-H\" isPdfEmbedded=\"true\"/>");
			return sb.toString();
		}

		public String parseQueryDefine() {
			StringBuffer sb = new StringBuffer();
			for (IColumn col : columns) {
				sb.append("<field name=\"").append(col.getProperty()).append("\"  class=\"java.lang.String\"/>");
			}
			return sb.toString();
		}

		public String parseBackGround() {
			StringBuffer sb = new StringBuffer();
			sb.append("<background><band height=\"0\"  isSplitAllowed=\"true\" ></band></background>");
			return sb.toString();
		}

		public String parseTitle() {
			StringBuffer sb = new StringBuffer();
			sb.append("<title>");
			sb.append("<band height=\"77\"  isSplitAllowed=\"true\" >");
			sb.append("<staticText>");
			// define reportElement
			sb.append("<reportElement mode=\"Transparent\" x=\"0\"  y=\"0\" width=\"535\" height=\"77\"").append(
					" forecolor=\"#000000\" backcolor=\"#FFFFFF\" key=\"staticText-1\" stretchType=\"NoStretch\"").append(
					" positionType=\"FixRelativeToTop\" isPrintRepeatedValues=\"true\" isRemoveLineWhenBlank=\"false\"").append(
					" isPrintInFirstWholeBand=\"false\" isPrintWhenDetailOverflows=\"false\" />");
			// define box
			sb.append("<box topBorder=\"None\" topBorderColor=\"#000000\" leftBorder=\"None\"").append(
					" leftBorderColor=\"#000000\" rightBorder=\"None\" rightBorderColor=\"#000000\"").append(
					" bottomBorder=\"None\" bottomBorderColor=\"#000000\"/>");
			// define text element
			sb.append("<textElement textAlignment=\"Center\" verticalAlignment=\"Middle\" rotation=\"None\" lineSpacing=\"Single\">");
			sb.append(" <font reportFont=\"TitleFont\"/>");
			sb.append("</textElement>");
			// define text
			sb.append("<text><![CDATA[").append(reportDescriptor.getHeaderTitle()).append("]]></text>");
			// end define static text
			sb.append("</staticText>").append("</band>").append("</title>");
			return sb.toString();
		}

		public String parsePageheader() {
			StringBuffer sb = new StringBuffer();
			sb.append("<pageHeader><band height=\"0\"  isSplitAllowed=\"true\" ></band></pageHeader>");
			return sb.toString();
		}

		public String parseColumnHeader() {
			String firstColBox = "<box topBorder=\"Thin\" topBorderColor=\"#000000\" leftBorder=\"Thin\"" + " leftBorderColor=\"#000000\" rightBorder=\"Thin\" "
					+ "rightBorderColor=\"#000000\" bottomBorder=\"Thin\"" + " bottomBorderColor=\"#000000\"/>";

			String otherColBox = "<box topBorder=\"Thin\" topBorderColor=\"#000000\" leftBorder=\"None\"" + " leftBorderColor=\"#000000\" rightBorder=\"Thin\" "
					+ "rightBorderColor=\"#000000\" bottomBorder=\"Thin\"" + " bottomBorderColor=\"#000000\"/>";

			StringBuffer sb = new StringBuffer();
			sb.append("<columnHeader><band height=\"" + this.getHeaderHeight() + "\"  isSplitAllowed=\"true\" >");

			int size = columns.size();
			int colWidth = DEFAULT_REPORT_WIDTH / size;

			for (int i = 0; i < size; i++) {
				IColumn col = columns.get(i);
				int start = i * colWidth;
				sb.append("<staticText>");
				sb.append("<reportElement mode=\"Transparent\" x=\"").append(start).append("\" y=\"0\" width=\"").append(colWidth).append("\"").append(
						" height=\"" + this.getHeaderHeight() + "\" forecolor=\"#000000\" backcolor=\"#FFFFFF\"").append(" key=\"staticHeaderText-").append(i)
						.append("\" stretchType=\"NoStretch\" positionType=\"FixRelativeToTop\"").append(
								" isPrintRepeatedValues=\"true\" isRemoveLineWhenBlank=\"false\"").append(
								" isPrintInFirstWholeBand=\"false\" isPrintWhenDetailOverflows=\"false\"/>");

				if (i == 0) {
					sb.append(firstColBox);
				} else {
					sb.append(otherColBox);
				}

				sb.append("<textElement textAlignment=\"Center\" verticalAlignment=\"Middle\"" + " rotation=\"None\" lineSpacing=\"Single\">");

				sb.append("<font reportFont=\"HeaderFont\"/>");
				sb.append("</textElement>");
				sb.append("<text><![CDATA[").append(col.getDisplayTitle()).append("]]>").append("</text></staticText>");
			}

			sb.append("	</band></columnHeader>");
			return sb.toString();
		}

		public String parseColumnFooter() {
			StringBuffer sb = new StringBuffer();
			sb.append("<columnFooter><band height=\"0\"  isSplitAllowed=\"true\" ></band></columnFooter>");
			return sb.toString();
		}

		public String parsePageFooter() {
			StringBuffer sb = new StringBuffer();
			sb.append("<pageFooter><band height=\"0\"  isSplitAllowed=\"true\" ></band></pageFooter>");
			return sb.toString();
		}

		public String parseLastPageFooter() {
			StringBuffer sb = new StringBuffer();
			sb.append("<lastPageFooter><band height=\"0\"  isSplitAllowed=\"true\" ></band></lastPageFooter>");
			return sb.toString();
		}

		public String parseSummary() {
			StringBuffer sb = new StringBuffer();
			sb.append("<summary><band height=\"0\"  isSplitAllowed=\"true\" ></band></summary>");
			sb.append("</jasperReport>");
			return sb.toString();
		}
	}
}
